//--------------------------------------------------------------------------------------
// Globals
//--------------------------------------------------------------------------------------
cbuffer CB// : register( b0 )
{
    matrix      g_mWorldViewProjection;    
    float4      g_vOutputColor; 
    float       g_fUStride;
    float       g_fUOffset;
    float2      g_vpadding;
};

//--------------------------------------------------------------------------------------
// Textures and Samplers
//--------------------------------------------------------------------------------------
Texture2D   g_txDiffuse : register( t0 );
SamplerState g_samLinear : register( s0 );


//--------------------------------------------------------------------------------------
// Input / Output structures
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
	float4 vCtrlPos	: CtrlPos;
    float4 vPrePos  : PrePos;
    float4 vPostPos : PostPos;
    uint id         : SV_VertexID;
};

struct VS_OUTPUT
{
    float4 vCtrlPos   : CtrlPos;
    float4 vPrePos    : PrePos;
    float4 vPostPos   : PostPos;
    float fUStride    : US;
    float fUOffset    : UO;
};

struct HS_CONSTANT_DATA_OUTPUT
{
    float Edges[4]     : SV_TessFactor;
    float Inside[2]    : SV_InsideTessFactor;
};

struct HS_OUTPUT
{
    float4 vCtrlPos  : CtrlPos;
    float4 vPrePos   : PrePos;
    float4 vPostPos  : PostPos;
    float fUStride    : US;
    float fUOffset    : UO;
};

struct DS_OUTPUT
{
    float4 vPosition  : SV_POSITION;
    float2 vTexcoord  : TEXCOORD0;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VSMain( VS_INPUT Input )
{	
	VS_OUTPUT Out;
	Out.vCtrlPos = mul( Input.vCtrlPos, g_mWorldViewProjection );
    Out.vPrePos = mul( Input.vPrePos, g_mWorldViewProjection );
    Out.vPostPos = mul( Input.vPostPos, g_mWorldViewProjection );

    Out.fUStride = g_fUStride;
    Out.fUOffset = Input.id / 2 * g_fUStride + g_fUOffset;

	return Out;
}

//--------------------------------------------------------------------------------------
// Hull Shader
//--------------------------------------------------------------------------------------
#define INPUT_PATCH_SIZE 2
#define OUTPUT_PATCH_SIZE 2

#ifndef TESS_AMOUNT
#define TESS_AMOUNT 1
#endif //TESS_AMOUNT

HS_CONSTANT_DATA_OUTPUT ConstantHS( InputPatch<VS_OUTPUT, INPUT_PATCH_SIZE> ip,
                                          uint PatchID : SV_PrimitiveID )
{    
    HS_CONSTANT_DATA_OUTPUT Output;

    Output.Inside[0] = Output.Edges[1] = Output.Edges[3] = TESS_AMOUNT;
	Output.Inside[1] = Output.Edges[0] = Output.Edges[2] = 2;
    
    return Output;
}


//integer
//fractional_even
//fractional_odd
//--------------------------------------------------------------------------------------
[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(OUTPUT_PATCH_SIZE)]
[patchconstantfunc("ConstantHS")]
HS_OUTPUT HSMain( InputPatch<VS_OUTPUT, INPUT_PATCH_SIZE> p, 
                    uint i : SV_OutputControlPointID,
                    uint PatchID : SV_PrimitiveID )
{
    HS_OUTPUT Output;
    Output.vCtrlPos = p[i].vCtrlPos;
    Output.vPrePos = p[i].vPrePos;
    Output.vPostPos = p[i].vPostPos;

    Output.fUStride = p[i].fUStride;
    Output.fUOffset = p[i].fUOffset;

    return Output;
}


//--------------------------------------------------------------------------------------
// Domain Shader
//--------------------------------------------------------------------------------------
float3 BernsteinBasis(float t)
{
    float invT = 1.0f - t;

    return float3( invT * invT, 2.0f * t * invT, t * t );
}


//--------------------------------------------------------------------------------------
float4 EvaluateBezier( const OutputPatch<HS_OUTPUT, OUTPUT_PATCH_SIZE> bezpatch,
                       float3 BasisU,
                       float2 BasisV )
{
    float4 Value = float4(0,0,0,0);

    Value  = BasisV.x * ( 
            bezpatch[0].vPrePos * BasisU.x + 
            bezpatch[0].vCtrlPos * BasisU.y + 
            bezpatch[0].vPostPos * BasisU.z );

    Value += BasisV.y * ( 
            bezpatch[1].vPrePos * BasisU.x + 
            bezpatch[1].vCtrlPos * BasisU.y + 
            bezpatch[1].vPostPos * BasisU.z );
    
    return Value;
}


[domain("quad")]
DS_OUTPUT DSMain( HS_CONSTANT_DATA_OUTPUT input, 
                    float2 UV : SV_DomainLocation,
                    const OutputPatch<HS_OUTPUT, OUTPUT_PATCH_SIZE> bezpatch )
{
    float3 BasisU = BernsteinBasis( UV.x );
    float2 LinearV = float2( UV.y, 1 - UV.y );

    float4 WorldPos = EvaluateBezier( bezpatch, BasisU, LinearV );

    DS_OUTPUT Output;
	Output.vPosition = WorldPos;

    Output.vTexcoord.x = bezpatch[0].fUStride * UV.x + bezpatch[0].fUOffset;
    Output.vTexcoord.y = UV.y;
    return Output;    
}

//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PSMain( DS_OUTPUT Input ) : SV_TARGET
{
    #ifdef TEXTURE   
       return g_vOutputColor * g_txDiffuse.Sample( g_samLinear, Input.vTexcoord );
    #else
       return g_vOutputColor;
    #endif
}
/**/